using System;
namespace ch.ethz.ssh2.channel
{
	
	/// <summary> Channel.
	/// 
	/// </summary>
	/// <author>  Christian Plattner
	/// </author>
	/// <version>  2.50, 03/15/10
	/// </version>
	public class Channel
	{
		virtual public ChannelInputStream StderrStream
		{
			/* Methods to allow access from classes outside of this package */
			
			
			get
			{
				return stderrStream;
			}
			
		}
		virtual public ChannelOutputStream StdinStream
		{
			get
			{
				return stdinStream;
			}
			
		}
		virtual public ChannelInputStream StdoutStream
		{
			get
			{
				return stdoutStream;
			}
			
		}
		virtual public System.String ExitSignal
		{
			get
			{
				lock (this)
				{
					return exit_signal;
				}
			}
			
		}
		virtual public System.Int32 ExitStatus
		{
			get
			{
				lock (this)
				{
					return exit_status;
				}
			}
			
		}
		virtual public System.String ReasonClosed
		{
			get
			{
				lock (reasonClosedLock)
				{
					return reasonClosed;
				}
			}
			
			set
			{
				lock (reasonClosedLock)
				{
					if (this.reasonClosed == null)
						this.reasonClosed = value;
				}
			}
			
		}
		/*
		* OK. Here is an important part of the JVM Specification:
		* (http://java.sun.com/docs/books/vmspec/2nd-edition/html/Threads.doc.html#22214)
		* 
		* Any association between locks and variables is purely conventional.
		* Locking any lock conceptually flushes all variables from a thread's
		* working memory, and unlocking any lock forces the writing out to main
		* memory of all variables that the thread has assigned. That a lock may be
		* associated with a particular object or a class is purely a convention.
		* (...)
		* 
		* If a thread uses a particular shared variable only after locking a
		* particular lock and before the corresponding unlocking of that same lock,
		* then the thread will read the shared value of that variable from main
		* memory after the lock operation, if necessary, and will copy back to main
		* memory the value most recently assigned to that variable before the
		* unlock operation.
		* 
		* This, in conjunction with the mutual exclusion rules for locks, suffices
		* to guarantee that values are correctly transmitted from one thread to
		* another through shared variables.
		* 
		* ====> Always keep that in mind when modifying the Channel/ChannelManger
		* code.
		* 
		*/
		
		internal const int STATE_OPENING = 1;
		internal const int STATE_OPEN = 2;
		internal const int STATE_CLOSED = 4;
		
		internal const int CHANNEL_BUFFER_SIZE = 30000;
		
		/*
		* To achieve correctness, the following rules have to be respected when
		* accessing this object:
		*/
		
		// These fields can always be read
		//UPGRADE_NOTE: Final was removed from the declaration of 'cm '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
		internal ChannelManager cm;
		//UPGRADE_NOTE: Final was removed from the declaration of 'stdinStream '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
		internal ChannelOutputStream stdinStream;
		//UPGRADE_NOTE: Final was removed from the declaration of 'stdoutStream '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
		internal ChannelInputStream stdoutStream;
		//UPGRADE_NOTE: Final was removed from the declaration of 'stderrStream '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
		internal ChannelInputStream stderrStream;
		
		// These two fields will only be written while the Channel is in state
		// STATE_OPENING.
		// The code makes sure that the two fields are written out when the state is
		// changing to STATE_OPEN.
		// Therefore, if you know that the Channel is in state STATE_OPEN, then you
		// can read these two fields without synchronizing on the Channel. However, make
		// sure that you get the latest values (e.g., flush caches by synchronizing on any
		// object). However, to be on the safe side, you can lock the channel.
		
		internal int localID = - 1;
		internal int remoteID = - 1;
		
		/*
		* Make sure that we never send a data/EOF/WindowChange msg after a CLOSE
		* msg.
		* 
		* This is a little bit complicated, but we have to do it in that way, since
		* we cannot keep a lock on the Channel during the send operation (this
		* would block sometimes the receiver thread, and, in extreme cases, can
		* lead to a deadlock on both sides of the connection (senders are blocked
		* since the receive buffers on the other side are full, and receiver
		* threads wait for the senders to finish). It all depends on the
		* implementation on the other side. But we cannot make any assumptions, we
		* have to assume the worst case. Confused? Just believe me.
		*/
		
		/*
		* If you send a message on a channel, then you have to aquire the
		* "channelSendLock" and check the "closeMessageSent" flag (this variable
		* may only be accessed while holding the "channelSendLock" !!!
		* 
		* BTW: NEVER EVER SEND MESSAGES FROM THE RECEIVE THREAD - see explanation
		* above.
		*/
		
		//UPGRADE_NOTE: Final was removed from the declaration of 'channelSendLock '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
		internal System.Object channelSendLock = new System.Object();
		internal bool closeMessageSent = false;
		
		/*
		* Stop memory fragmentation by allocating this often used buffer.
		* May only be used while holding the channelSendLock
		*/
		
		//UPGRADE_NOTE: Final was removed from the declaration of 'msgWindowAdjust '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
		internal byte[] msgWindowAdjust = new byte[9];
		
		// If you access (read or write) any of the following fields, then you have
		// to synchronize on the channel.
		
		internal int state = STATE_OPENING;
		
		internal bool closeMessageRecv = false;
		
		/* This is a stupid implementation. At the moment we can only wait
		* for one pending request per channel.
		*/
		internal int successCounter = 0;
		internal int failedCounter = 0;
		
		internal int localWindow = 0; /* locally, we use a small window, < 2^31 */
		internal long remoteWindow = 0; /* long for readable  2^32 - 1 window support */
		
		internal int localMaxPacketSize = - 1;
		internal int remoteMaxPacketSize = - 1;
		
		//UPGRADE_NOTE: Final was removed from the declaration of 'stdoutBuffer '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
		internal byte[] stdoutBuffer = new byte[CHANNEL_BUFFER_SIZE];
		//UPGRADE_NOTE: Final was removed from the declaration of 'stderrBuffer '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
		internal byte[] stderrBuffer = new byte[CHANNEL_BUFFER_SIZE];
		
		internal int stdoutReadpos = 0;
		internal int stdoutWritepos = 0;
		internal int stderrReadpos = 0;
		internal int stderrWritepos = 0;
		
		internal bool EOF = false;
		
		internal System.Int32 exit_status;
		
		internal System.String exit_signal;
		
		// we keep the x11 cookie so that this channel can be closed when this
		// specific x11 forwarding gets stopped
		
		internal System.String hexX11FakeCookie;
		
		// reasonClosed is special, since we sometimes need to access it
		// while holding the channelSendLock.
		// We protect it with a private short term lock.
		
		//UPGRADE_NOTE: Final was removed from the declaration of 'reasonClosedLock '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
		private System.Object reasonClosedLock = new System.Object();
		private System.String reasonClosed = null;
		
		public Channel(ChannelManager cm)
		{
			this.cm = cm;
			
			this.localWindow = CHANNEL_BUFFER_SIZE;
			this.localMaxPacketSize = 35000 - 1024; // leave enough slack
			
			this.stdinStream = new ChannelOutputStream(this);
			this.stdoutStream = new ChannelInputStream(this, false);
			this.stderrStream = new ChannelInputStream(this, true);
		}
	}
}